/*
 * Copyright (c) 2010 Philip Foulkes <philip.foulkes@gmail.com>
 *
 * This file is free software and is made available under the
 * terms of the GNU General Public License, version 2, or at your
 * option, any later version
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/if_ether.h>
#include <linux/hardirq.h>

#include "mrp_private.h"
#include "mrp_mrpdu.h"

const u8 mrp_application_address_mmrp [6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x20 } ;
const u8 mrp_application_address_mvrp [6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x21 } ;
const u8 mrp_application_address_msrp [6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x22 } ;

EXPORT_SYMBOL (mrp_application_address_mmrp);
EXPORT_SYMBOL (mrp_application_address_mvrp);
EXPORT_SYMBOL (mrp_application_address_msrp);

/*
*
*/
static inline int pack_into_buff (struct mrp_mrpdu * mrp_mrpdu, void * data, u32 length)
{
	if (skb_tailroom (mrp_mrpdu->skb) < length)
		return -ERANGE;

	memcpy (skb_put(mrp_mrpdu->skb, length), data, length);
	return 0;
}

/*
*
*/
/*static inline int unpack_from_buff (struct mrp_mrpdu * mrp_mrpdu, void * data, u32 length)
{
	if (mrp_mrpdu->skb->len < length)
		return -ERANGE;

	memcpy (data, mrp_mrpdu->skb->data, length);
	skb_pull(mrp_mrpdu->skb, length);
	return 0;
}*/

/*
*
*/
static inline int read_from_curr_and_inc_curr (struct mrp_mrpdu * mrp_mrpdu, void * data, u32 length)
{
	if (mrp_mrpdu->curr < mrp_mrpdu->data)
		return -EINVAL;

	if ((skb_tail_pointer(mrp_mrpdu->skb) - mrp_mrpdu->curr) < length)
		return -ERANGE;

	memcpy (data, mrp_mrpdu->curr, length);
	mrp_mrpdu->curr += length;
	return 0;
}

void reset_curr (struct mrp_mrpdu * mrp_mrpdu)
{
	mrp_mrpdu->curr = mrp_mrpdu->data;
}

/**
*
*/
struct mrp_mrpdu * mrp_mrpdu_create_and_init(struct mrp * mrp, int mrpdu_length, int use_attribute_list_length)
{
	struct sk_buff * skb;
	struct mrp_mrpdu * mrp_mrpdu;

	if ( ! (skb = alloc_skb (ETH_HLEN + mrpdu_length, MRP_GET_CONTEXT_ALLOC_FLAG)) )
		return NULL;

	skb_reserve (skb, ETH_HLEN); /* reserve some space for the ethernet header */

	if ((mrp_mrpdu = mrp_mrpdu_create_and_init_with_supplied_sk_buff (mrp, skb, 1, use_attribute_list_length)))
		return mrp_mrpdu;
	else
	{
		kfree_skb (skb);
		return NULL;
	}
}

/**
*
*/
struct mrp_mrpdu * mrp_mrpdu_create_and_init_with_supplied_sk_buff (struct mrp * mrp, struct sk_buff * skb, int own_sk_buff, int use_attribute_list_length)
{
	struct mrp_mrpdu * mrpdu;

	if ( ! (mrpdu = kzalloc (sizeof(struct mrp_mrpdu), MRP_GET_CONTEXT_ALLOC_FLAG)) )
		return NULL;

	mrpdu->skb = skb;
	mrpdu->owns_skb = own_sk_buff;
	mrpdu->data = skb->data; /* point to beginning of the MRPDU */
	mrpdu->mrp = mrp;
	mrpdu->use_attribute_list_length = use_attribute_list_length;
	mrpdu->last_attribute_list_length = NULL;
	reset_curr (mrpdu);

	return mrpdu;
}

/**
*
*/
void mrp_mrpdu_destroy (struct mrp_mrpdu ** mrp_mrpdu)
{
	struct mrp_mrpdu * mrpdu;

	mrpdu = *mrp_mrpdu;

	if (mrpdu)
	{
		if (mrpdu->owns_skb && mrpdu->skb)
			kfree_skb (mrpdu->skb);
		kfree (mrpdu);
		*mrp_mrpdu = NULL;
	}
}

/**
*
*/
int mrp_mrpdu_does_use_attribute_list_length (struct mrp_mrpdu * mrp_mrpdu)
{
	return mrp_mrpdu->use_attribute_list_length;
}

/**
*
*/
u8 inline * mrp_mrpdu_get_data (struct mrp_mrpdu * mrp_mrpdu)
{
	return mrp_mrpdu->data;
}

/**
*
*/
int inline mrp_mrpdu_get_data_length (struct mrp_mrpdu * mrp_mrpdu)
{
	return skb_tail_pointer(mrp_mrpdu->skb) - mrp_mrpdu->data;
}

/**
*
*/
int mrp_mrpdu_peek (struct mrp_mrpdu * mrp_mrpdu, void * data, int length)
{
	if (skb_tail_pointer(mrp_mrpdu->skb) - mrp_mrpdu->curr  < length)
		return -ERANGE;

	memcpy (data, mrp_mrpdu->curr, length);
	return 0;
}

int mrp_mrpdu_pack_test_mrpdu (struct mrp_mrpdu * mrpdu)
{
	int res;

	u8 protocol_version;
	u8 attribute_type;
	u8 attribute_length;
	u8 leave_all_event;
	u16 number_of_values;
	u32 first_value;
	u8 attribute_event1;
	u8 attribute_event2;
	u8 attribute_event3;
	u16 end_mark;

	protocol_version = 0;
	if ((res = mrp_mrpdu_set_protocol_version (mrpdu, protocol_version)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_set_protocol_version");
		return res;
	}

// message 01 ================================================================================================
// ===========================================================================================================
	attribute_type = 1;
	if ((res = mrp_mrpdu_add_attribute_type (mrpdu, attribute_type)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_attribute_type");
		return res;
	}

	attribute_length = 4;
	if ((res = mrp_mrpdu_add_attribute_length (mrpdu, attribute_length)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_attribute_length");
		return res;
	}


// vector attribute 01 ==============================================================================
	leave_all_event = MRP_LEAVE_ALL_EVENT_LeaveAll;
	number_of_values = 6;
	if ((res = mrp_mrpdu_add_vector_header (mrpdu, leave_all_event, number_of_values)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_vector_header");
		return res;
	}

	first_value = 0x1;

	if ((res = mrp_mrpdu_add_first_value (mrpdu, &first_value, sizeof(first_value))))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_first_value");
		return res;
	}

	attribute_event1 = MRP_ATTRIBUTE_EVENT_New;
	attribute_event2 = MRP_ATTRIBUTE_EVENT_JoinIn;
	attribute_event3 = MRP_ATTRIBUTE_EVENT_In;
	if ((res = mrp_mrpdu_add_three_packed_events (mrpdu, attribute_event1, attribute_event2, attribute_event3)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_three_packed_events");
		return res;
	}

	if ((res = mrp_mrpdu_add_three_packed_events (mrpdu, attribute_event1, attribute_event2, attribute_event3)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_three_packed_events");
		return res;
	}
// ================================================================================================



// vector attribute 02 ==============================================================================
	if ((res = mrp_mrpdu_add_vector_header (mrpdu, leave_all_event, number_of_values)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_vector_header");
		return res;
	}

	first_value = 0x10111213;

	if ((res = mrp_mrpdu_add_first_value (mrpdu, &first_value, sizeof(first_value))))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_first_value");
		return res;
	}

	if ((res = mrp_mrpdu_add_three_packed_events (mrpdu, attribute_event1, attribute_event2, attribute_event3)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_three_packed_events");
		return res;
	}

	if ((res = mrp_mrpdu_add_three_packed_events (mrpdu, attribute_event1, attribute_event2, attribute_event3)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_three_packed_events");
		return res;
	}
// ================================================================================================

	end_mark = 0;
	if ((res = mrp_mrpdu_add_attribute_list_end_mark (mrpdu, end_mark)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_end_mark");
		return res;
	}

// ============================================================================================================
// ============================================================================================================


// message 02 ================================================================================================
// ===========================================================================================================
	if ((res = mrp_mrpdu_add_attribute_type (mrpdu, attribute_type)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_attribute_type");
		return res;
	}

	if ((res = mrp_mrpdu_add_attribute_length (mrpdu, 4)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_attribute_length");
		return res;
	}


// vector attribute 01 ==============================================================================
	if ((res = mrp_mrpdu_add_vector_header (mrpdu, leave_all_event, number_of_values)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_vector_header");
		return res;
	}

	first_value = 0x14151617;

	if ((res = mrp_mrpdu_add_first_value (mrpdu, &first_value, sizeof(first_value))))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_first_value");
		return res;
	}

	if ((res = mrp_mrpdu_add_three_packed_events (mrpdu, attribute_event1, attribute_event2, attribute_event3)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_three_packed_events");
		return res;
	}

	if ((res = mrp_mrpdu_add_three_packed_events (mrpdu, attribute_event1, attribute_event2, attribute_event3)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_three_packed_events");
		return res;
	}
// ================================================================================================



// vector attribute 02 ==============================================================================
	if ((res = mrp_mrpdu_add_vector_header (mrpdu, leave_all_event, number_of_values)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_vector_header");
		return res;
	}

	first_value = 0x18191A1B;

	if ((res = mrp_mrpdu_add_first_value (mrpdu, &first_value, sizeof(first_value))))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_first_value");
		return res;
	}

	if ((res = mrp_mrpdu_add_three_packed_events (mrpdu, attribute_event1, attribute_event2, attribute_event3)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_three_packed_events");
		return res;
	}

	if ((res = mrp_mrpdu_add_three_packed_events (mrpdu, attribute_event1, attribute_event2, attribute_event3)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_three_packed_events");
		return res;
	}
// ================================================================================================

	if ((res = mrp_mrpdu_add_attribute_list_end_mark (mrpdu, end_mark)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_end_mark");
		return res;
	}

// ============================================================================================================
// ============================================================================================================

	if ((res = mrp_mrpdu_add_mrpdu_end_mark (mrpdu, end_mark)))
	{
		MRP_PRINT_INFO ("%s\n", "FAIL mrp_mrpdu_add_end_mark");
		return res;
	}

	return 0;
}

/**
*
*/
int mrp_mrpdu_print (struct mrp_mrpdu * mrp_mrpdu)
{
	int ret;

	int num_messages;
	int num_vector_attributes;
	int num_attribute_events_left;
	int i;

	u8 protocol_version;
	u8 attribute_type;
	u8 attribute_length;
	u16 attribute_list_length;
	u8 leave_all_event;
	u16 number_of_values;
	u8 * first_value;
	u8 attribute_event1;
	u8 attribute_event2;
	u8 attribute_event3;
	u8 four_packed_type1;
	u8 four_packed_type2;
	u8 four_packed_type3;
	u8 four_packed_type4;
	u16 end_mark;
	u16 end_mark_peek;

	num_messages = 0;

	reset_curr (mrp_mrpdu);

// protocol version
	if ((ret = mrp_mrpdu_get_protocol_version (mrp_mrpdu, &protocol_version))) {
		MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "failed at mrp_mrpdu_get_protocol_version"); return ret;
	}
	MRP_PRINT_INFO ("%s %x\n", "Protocol Version: ", protocol_version);
	MRP_PRINT_INFO ("\n");

	do {
	// message

		MRP_PRINT_INFO (" %s %i\n", "Message", ++num_messages);

		if ((ret = mrp_mrpdu_get_attribute_type (mrp_mrpdu, &attribute_type))) {
			MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "failed at mrp_mrpdu_get_attribute_type"); return ret;
		}
		MRP_PRINT_INFO ("  %s 0x%X\n", "Attribute type", attribute_type);

		if ((ret = mrp_mrpdu_get_attribute_length (mrp_mrpdu, &attribute_length))) {
			MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "failed at mrp_mrpdu_get_attribute_type"); return ret;
		}
		MRP_PRINT_INFO ("  %s %i\n", "Attribute length", attribute_length);

		if (mrp_mrpdu->use_attribute_list_length)
		{
			if ((ret = mrp_mrpdu_get_attribute_list_length (mrp_mrpdu, &attribute_list_length))) {
				MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "failed at mrp_mrpdu_get_attribute_list_length"); return ret;
			}
			MRP_PRINT_INFO ("  %s %i\n", "Attribute list length", attribute_list_length);
		}
		else
			attribute_list_length = 0;

		MRP_PRINT_INFO ("  %s\n", "Attribute list");
		num_vector_attributes = 0;

		do {
			MRP_PRINT_INFO ("   %s %i\n", "Vector attribute", ++num_vector_attributes);

			MRP_PRINT_INFO ("    %s\n", "Vector header");

			if ((ret = mrp_mrpdu_get_vector_header (mrp_mrpdu, &leave_all_event, &number_of_values))) {
				MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "failed at mrp_mrpdu_get_vector_header"); return ret;
			}

			MRP_PRINT_INFO ("     %s: %i %s: %i\n", "Leave all event", leave_all_event, "Number of values", number_of_values);

			if (number_of_values > 0)
			{
				first_value = kmalloc (mrp_mrpdu->last_attribute_length, MRP_GET_CONTEXT_ALLOC_FLAG);
				if ((ret = mrp_mrpdu_get_first_value (mrp_mrpdu, first_value, mrp_mrpdu->last_attribute_length))) {
					MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "failed at mrp_mrpdu_get_first_value"); kfree (first_value); return ret;
				}
				MRP_PRINT_INFO ("    %s", "First value");
				for (i = 0; i < mrp_mrpdu->last_attribute_length; ++i)
					printk (KERN_INFO " 0x%X ", *(first_value + i));
				kfree (first_value);
				printk (KERN_INFO "\n");

				MRP_PRINT_INFO ("    %s\n", "Vector");

				num_attribute_events_left = number_of_values;

				do {
					MRP_PRINT_INFO ("     %s\n", "Three packed events");
					if ((ret = mrp_mrpdu_get_three_packed_events (mrp_mrpdu, &attribute_event1, &attribute_event2, &attribute_event3))) {
						MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "failed at mrp_mrpdu_get_three_apcked_events"); return ret;
					}
					if (num_attribute_events_left >= 1)
					{
						MRP_PRINT_INFO ("      %s: %i\n", "Attribute event 01", attribute_event1);
						if (num_attribute_events_left >= 2)
						{
							MRP_PRINT_INFO ("      %s: %i\n", "Attribute event 02", attribute_event2);
							if (num_attribute_events_left >= 3)
								MRP_PRINT_INFO ("      %s: %i\n", "Attribute event 03", attribute_event3);
						}
					}

					num_attribute_events_left -= 3;

				} while (num_attribute_events_left > 0) ;

				if (mrp_mrpdu->mrp->mrp_ops->mrp_does_need_four_packed_events (attribute_type))
				{
					num_attribute_events_left = number_of_values;

					do {
						MRP_PRINT_INFO ("     %s\n", "Four packed events");
						if ((ret = mrp_mrpdu_get_four_packed_events (mrp_mrpdu, &four_packed_type1, &four_packed_type2, &four_packed_type3, &four_packed_type4))) {
							MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "failed at mrp_mrpdu_get_four_packed_events"); return ret;
						}
						if (num_attribute_events_left >= 1)
						{
							MRP_PRINT_INFO ("      %s: %i\n", "Four packed type 01", four_packed_type1);
							if (num_attribute_events_left >= 2)
							{
								MRP_PRINT_INFO ("      %s: %i\n", "Four packed type 02", four_packed_type2);
								if (num_attribute_events_left >= 3)
								{
									MRP_PRINT_INFO ("      %s: %i\n", "Four packed type 03", four_packed_type3);
									if (num_attribute_events_left >= 4)
										MRP_PRINT_INFO ("      %s: %i\n", "Four packed type 04", four_packed_type4);
								}
							}
						}

						num_attribute_events_left -= 4;

					} while (num_attribute_events_left > 0) ;
				}
			}

			if ((ret = mrp_mrpdu_peek (mrp_mrpdu, &end_mark_peek, MRP_MRPDU_END_MARK_SIZE))) {
				MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "failed at mrp_mrpdu_peek"); return ret;
			}
		} while (end_mark_peek != MRP_MRPDU_EndMark) ;

		if ((ret = mrp_mrpdu_get_end_mark (mrp_mrpdu, &end_mark))) {
			MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "mrp_mrpdu_get_end_mark"); return ret;
		}
		MRP_PRINT_INFO ("  %s\n", "End mark");

		MRP_PRINT_INFO (" %s %i\n", "End of Message", num_messages);
		MRP_PRINT_INFO ("\n");

		if ((ret = mrp_mrpdu_peek (mrp_mrpdu, &end_mark_peek, MRP_MRPDU_END_MARK_SIZE))) {
			MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "failed at mrp_mrpdu_peek"); return ret;
		}
	} while (end_mark_peek != MRP_MRPDU_EndMark) ;

	if ((ret = mrp_mrpdu_get_end_mark (mrp_mrpdu, &end_mark))) {
		MRP_PRINT_INFO ("%s %s\n", __FUNCTION__, "mrp_mrpdu_get_end_mark"); return ret;
	}
	MRP_PRINT_INFO (" %s\n", "End mark");

	MRP_PRINT_INFO ("%s\n", "End of MRPDU");

	return 0;
}

/**
*
*/
struct sk_buff * mrp_mrpdu_get_sk_buff (struct mrp_mrpdu * mrp_mrpdu, int disown_skb)
{
	if (disown_skb)
		mrp_mrpdu->owns_skb = 0;
	return mrp_mrpdu->skb;
}

/**
*
*/
int mrp_mrpdu_set_sk_buff_ownership (struct mrp_mrpdu * mrp_mrpdu, int owns_skb)
{
	mrp_mrpdu->owns_skb = owns_skb;
	return 0;
}

/**
*
*/
struct sk_buff * mrp_mrpdu_get_sk_buff_clone (struct mrp_mrpdu * mrp_mrpdu)
{
	return skb_clone (mrp_mrpdu->skb, GFP_KERNEL);
}

struct ethhdr * mrp_mrpdu_get_ethernet_header(struct mrp_mrpdu * mrp_mrpdu)
{
    return (struct ethhdr *)skb_mac_header(mrp_mrpdu->skb);
}

/* ======================================================================================================== */
int mrp_mrpdu_set_protocol_version (struct mrp_mrpdu * mrp_mrpdu, u8 protocol_version)
{
	return pack_into_buff (mrp_mrpdu, &protocol_version, MRP_MRPDU_PROTOCOL_VERSION_SIZE);
}

int mrp_mrpdu_add_attribute_type (struct mrp_mrpdu * mrp_mrpdu, u8 attribute_type)
{
	return pack_into_buff (mrp_mrpdu, &attribute_type, MRP_MRPDU_ATTRIBUTE_TYPE_SIZE);
}

static int mrp_mrpdu_add_attribute_list_length (struct mrp_mrpdu * mrp_mrpdu, u16 attribute_list_length);

int mrp_mrpdu_add_attribute_length (struct mrp_mrpdu * mrp_mrpdu, u8 attribute_length)
{
	int ret;
	mrp_mrpdu->last_attribute_length = attribute_length;

	if ( ! (ret = pack_into_buff (mrp_mrpdu, &attribute_length, MRP_MRPDU_ATTRIBUTE_LENGTH_SIZE)))
		if ( mrp_mrpdu->use_attribute_list_length && ! (ret = mrp_mrpdu_add_attribute_list_length (mrp_mrpdu, 0)) ) // dummy value for now, just move the tail pointer along
			mrp_mrpdu->last_attribute_list_length = skb_tail_pointer(mrp_mrpdu->skb) - MRP_MRPDU_ATTRIBUTE_LIST_LENGTH_SIZE;

	return ret;
}

static int mrp_mrpdu_add_attribute_list_length (struct mrp_mrpdu * mrp_mrpdu, u16 attribute_list_length)
{
	if (!mrp_mrpdu->use_attribute_list_length)
		return -EINVAL;
	attribute_list_length = htons(attribute_list_length);
	return pack_into_buff (mrp_mrpdu, &attribute_list_length, MRP_MRPDU_ATTRIBUTE_LIST_LENGTH_SIZE);
}

int mrp_mrpdu_add_vector_header (struct mrp_mrpdu * mrp_mrpdu, u8 leave_all_event, u16 number_of_values)
{
	u16 vector_header;

	if (
	#if (MRP_LEAVE_ALL_EVENT_MIN > 0)
		leave_all_event < MRP_LEAVE_ALL_EVENT_MIN ||
	#endif
		leave_all_event > MRP_LEAVE_ALL_EVENT_MAX)
		return -EINVAL;

	vector_header = htons((leave_all_event * 8192) + number_of_values);
	mrp_mrpdu->last_number_of_values = number_of_values;
	return pack_into_buff (mrp_mrpdu, &vector_header, MRP_MRPDU_VECTOR_HEADER_SIZE);
}

int mrp_mrpdu_add_first_value (struct mrp_mrpdu * mrp_mrpdu, void * first_value, int first_value_length)
{
	if (mrp_mrpdu->last_attribute_length != first_value_length)
		return -EINVAL;
	return pack_into_buff (mrp_mrpdu, first_value, first_value_length);
}

int mrp_mrpdu_add_three_packed_events (struct mrp_mrpdu * mrp_mrpdu, u8 attribute_event_1, u8 attribute_event_2, u8 attribute_event_3)
{
	u8 three_packed_events;

	if (
	#if (MRP_ATTRIBUTE_EVENT_MIN > 0)
			attribute_event_1 < MRP_ATTRIBUTE_EVENT_MIN
		|| 	attribute_event_2 < MRP_ATTRIBUTE_EVENT_MIN
		|| 	attribute_event_3 < MRP_ATTRIBUTE_EVENT_MIN ||
	#endif
			attribute_event_1 > MRP_ATTRIBUTE_EVENT_MAX
		|| 	attribute_event_2 > MRP_ATTRIBUTE_EVENT_MAX
		|| 	attribute_event_3 > MRP_ATTRIBUTE_EVENT_MAX )
	{
		return -EINVAL;
	}

	three_packed_events = (((((attribute_event_1) * 6) + attribute_event_2) * 6) + attribute_event_3);
	return pack_into_buff (mrp_mrpdu, &three_packed_events, MRP_MRPDU_THREE_PACKED_EVENTS_SIZE);
}

int mrp_mrpdu_add_four_packed_events (struct mrp_mrpdu * mrp_mrpdu, u8 four_packed_type_1, u8 four_packed_type_2, u8 four_packed_type_3, u8 four_packed_type_4)
{
	u8 four_packed_events;

	if (
	#if (MRP_FOUR_PACKED_TYPE_MIN > 0)
			four_packed_type_1 < MRP_FOUR_PACKED_TYPE_MIN
		||	four_packed_type_2 < MRP_FOUR_PACKED_TYPE_MIN
		||	four_packed_type_3 < MRP_FOUR_PACKED_TYPE_MIN
		||	four_packed_type_4 < MRP_FOUR_PACKED_TYPE_MIN ||
	#endif
			four_packed_type_1 > MRP_FOUR_PACKED_TYPE_MAX
		||	four_packed_type_2 > MRP_FOUR_PACKED_TYPE_MAX
		||	four_packed_type_3 > MRP_FOUR_PACKED_TYPE_MAX
		||	four_packed_type_4 > MRP_FOUR_PACKED_TYPE_MAX )
	{
		return -EINVAL;
	}

	four_packed_events = ((four_packed_type_1 * 64) + (four_packed_type_2 * 16) + (four_packed_type_3 * 4) + four_packed_type_4);
	return pack_into_buff (mrp_mrpdu, &four_packed_events, MRP_MRPDU_FOUR_PACKED_EVENTS_SIZE);
}

int mrp_mrpdu_add_attribute_list_end_mark (struct mrp_mrpdu * mrp_mrpdu, u16 end_mark)
{
	int ret;
	u16 attribute_list_length;

	if (end_mark != MRP_MRPDU_EndMark)
		return -EINVAL;

	end_mark = htons (end_mark);
	if ((ret = pack_into_buff (mrp_mrpdu, &end_mark, MRP_MRPDU_END_MARK_SIZE)))
		return ret;

	if (mrp_mrpdu->use_attribute_list_length)
	{
		attribute_list_length = htons (skb_tail_pointer(mrp_mrpdu->skb) - mrp_mrpdu->last_attribute_list_length - 2);
		memcpy (mrp_mrpdu->last_attribute_list_length, &attribute_list_length, MRP_MRPDU_ATTRIBUTE_LIST_LENGTH_SIZE);
	}

	return ret;
}

int mrp_mrpdu_add_mrpdu_end_mark (struct mrp_mrpdu * mrp_mrpdu, u16 end_mark)
{
	if (end_mark != MRP_MRPDU_EndMark)
		return -EINVAL;
	end_mark = htons (end_mark);
	return pack_into_buff (mrp_mrpdu, &end_mark, MRP_MRPDU_END_MARK_SIZE);
}
/* ======================================================================================================== */



/* ======================================================================================================== */
int mrp_mrpdu_get_protocol_version (struct mrp_mrpdu * mrp_mrpdu, MRP_OUT u8 * protocol_version)
{
	return read_from_curr_and_inc_curr (mrp_mrpdu, protocol_version, MRP_MRPDU_PROTOCOL_VERSION_SIZE);
}

int mrp_mrpdu_get_attribute_type (struct mrp_mrpdu * mrp_mrpdu, MRP_OUT u8 * attribute_type)
{
	return read_from_curr_and_inc_curr (mrp_mrpdu, attribute_type, MRP_MRPDU_ATTRIBUTE_TYPE_SIZE);
}

int mrp_mrpdu_get_attribute_length (struct mrp_mrpdu * mrp_mrpdu, MRP_OUT u8 * attribute_length)
{
	int ret;

	if (!(ret = read_from_curr_and_inc_curr (mrp_mrpdu, attribute_length, MRP_MRPDU_ATTRIBUTE_LENGTH_SIZE)))
		mrp_mrpdu->last_attribute_length = *attribute_length;
	else
		mrp_mrpdu->last_attribute_length = 0;
	return ret;
}

int mrp_mrpdu_get_attribute_list_length (struct mrp_mrpdu * mrp_mrpdu, MRP_OUT u16 * attribute_list_length)
{
	int ret;
	if (!mrp_mrpdu->use_attribute_list_length)
		return -EINVAL;
	if ((ret = read_from_curr_and_inc_curr (mrp_mrpdu, attribute_list_length, MRP_MRPDU_ATTRIBUTE_LIST_LENGTH_SIZE)))
		return ret;
	*attribute_list_length = ntohs (*attribute_list_length);
	return 0;
}

int mrp_mrpdu_get_vector_header (struct mrp_mrpdu * mrp_mrpdu, MRP_OUT u8 * leave_all_event, MRP_OUT u16 * number_of_values)
{
	u16 vector_header;
	int ret;

	if (!(ret = read_from_curr_and_inc_curr (mrp_mrpdu, &vector_header, MRP_MRPDU_VECTOR_HEADER_SIZE)))
	{
		vector_header = ntohs (vector_header);
		*leave_all_event =  (u8) (vector_header >> 13);
		*number_of_values = (u16)(vector_header & 0x1FFF);
		if (
		#if (MRP_LEAVE_ALL_EVENT_MIN > 0)
			*leave_all_event < MRP_LEAVE_ALL_EVENT_MIN ||
		#endif
			*leave_all_event > MRP_LEAVE_ALL_EVENT_MAX)
		{
			*leave_all_event  = 0;
			*number_of_values = 0;
			mrp_mrpdu->last_number_of_values = 0;
			return -EINVAL;
		}
		else
		{
			mrp_mrpdu->last_number_of_values = *number_of_values;
		}
	}

	return ret;
}

int mrp_mrpdu_get_first_value (struct mrp_mrpdu * mrp_mrpdu, MRP_OUT void * first_value, int first_value_length)
{
	if (mrp_mrpdu->last_attribute_length != first_value_length)
		return -EINVAL;
	return read_from_curr_and_inc_curr (mrp_mrpdu, first_value, first_value_length);
}

int mrp_mrpdu_get_three_packed_events (struct mrp_mrpdu * mrp_mrpdu, MRP_OUT u8 * attribute_event_1, MRP_OUT u8 * attribute_event_2, MRP_OUT u8 * attribute_event_3)
{
	int ret;
	u8 three_packed_events;

	if (!(ret = read_from_curr_and_inc_curr (mrp_mrpdu, &three_packed_events, MRP_MRPDU_THREE_PACKED_EVENTS_SIZE)))
		return mrp_mrpdu_get_three_packed_events_from_byte (three_packed_events, attribute_event_1, attribute_event_2, attribute_event_3);

	return ret;
}

int mrp_mrpdu_get_four_packed_events (struct mrp_mrpdu * mrp_mrpdu, MRP_OUT u8 * four_packed_type_1, MRP_OUT u8 * four_packed_type_2, MRP_OUT u8 * four_packed_type_3, MRP_OUT u8 * four_packed_type_4)
{
	int ret;
	u8 four_packed_events;

	if (!(ret = read_from_curr_and_inc_curr (mrp_mrpdu, &four_packed_events, MRP_MRPDU_FOUR_PACKED_EVENTS_SIZE)))
		return mrp_mrpdu_get_four_packed_events_from_byte (four_packed_events, four_packed_type_1, four_packed_type_2, four_packed_type_3, four_packed_type_4);

	return ret;
}

int mrp_mrpdu_get_end_mark (struct mrp_mrpdu * mrp_mrpdu, MRP_OUT u16 * end_mark)
{
	int ret;

	if (!(ret = read_from_curr_and_inc_curr (mrp_mrpdu, end_mark, MRP_MRPDU_END_MARK_SIZE)))
	{
		*end_mark = ntohs (*end_mark);
		if (*end_mark != MRP_MRPDU_EndMark)
			return -EINVAL;
	}

	return ret;
}
/* ======================================================================================================== */

int mrp_mrpdu_get_bytes (struct mrp_mrpdu * mrp_mrpdu, MRP_OUT u8 * bytes, int num_bytes)
{
	return read_from_curr_and_inc_curr (mrp_mrpdu, bytes, num_bytes);
}

int mrp_mrpdu_get_three_packed_events_from_byte (u8 three_packed_events, MRP_OUT u8 * attribute_event_1, MRP_OUT u8 * attribute_event_2, MRP_OUT u8 * attribute_event_3)
{
	*attribute_event_3 = three_packed_events % 6;
	three_packed_events -= *attribute_event_3;
	three_packed_events /= 6;

	*attribute_event_2 = three_packed_events % 6;
	three_packed_events -= *attribute_event_2;
	three_packed_events /= 6;

	*attribute_event_1 = three_packed_events;

	if (
	#if (MRP_ATTRIBUTE_EVENT_MIN > 0)
			*attribute_event_1 < MRP_ATTRIBUTE_EVENT_MIN
		|| 	*attribute_event_2 < MRP_ATTRIBUTE_EVENT_MIN
		|| 	*attribute_event_3 < MRP_ATTRIBUTE_EVENT_MIN ||
	#endif
			*attribute_event_1 > MRP_ATTRIBUTE_EVENT_MAX
		|| 	*attribute_event_2 > MRP_ATTRIBUTE_EVENT_MAX
		|| 	*attribute_event_3 > MRP_ATTRIBUTE_EVENT_MAX )
		return -EINVAL;

	return 0;
}

int mrp_mrpdu_get_four_packed_events_from_byte  (u8 four_packed_events,  MRP_OUT u8 * four_packed_type_1, MRP_OUT u8 * four_packed_type_2, MRP_OUT u8 * four_packed_type_3, MRP_OUT u8 * four_packed_type_4)
{
	*four_packed_type_4 = four_packed_events % 4;
	four_packed_events -= *four_packed_type_4;
	four_packed_events /= 4;

	*four_packed_type_3 = four_packed_events % 4;
	four_packed_events -= *four_packed_type_3;
	four_packed_events /= 4;

	*four_packed_type_2 = four_packed_events % 4;
	four_packed_events -= *four_packed_type_2;
	four_packed_events /= 4;

	*four_packed_type_1 = four_packed_events;

	if (
	#if (MRP_FOUR_PACKED_TYPE_MIN > 0)
			*four_packed_type_1 < MRP_FOUR_PACKED_TYPE_MIN
		||	*four_packed_type_2 < MRP_FOUR_PACKED_TYPE_MIN
		||	*four_packed_type_3 < MRP_FOUR_PACKED_TYPE_MIN
		||	*four_packed_type_4 < MRP_FOUR_PACKED_TYPE_MIN ||
	#endif
			*four_packed_type_1 > MRP_FOUR_PACKED_TYPE_MAX
		||	*four_packed_type_2 > MRP_FOUR_PACKED_TYPE_MAX
		||	*four_packed_type_3 > MRP_FOUR_PACKED_TYPE_MAX
		||	*four_packed_type_4 > MRP_FOUR_PACKED_TYPE_MAX )
		return -EINVAL;

	return 0;
}
